package com.slipper.bpmn;

import com.slipper.bpmn.dealer.INodeDealer;
import com.slipper.bpmn.dealer.NodeDealerFactory;
import com.slipper.codeless.service.CodelessService;
import com.slipper.constants.Constants;
import com.slipper.constants.FlowConstants;
import com.slipper.exception.CommonException;
import com.slipper.groovy.service.GroovyService;
import com.slipper.system.bpmn.bo.BpmnBO;
import com.slipper.bpmn.vo.BpmnVO;
import com.slipper.system.codeless.service.DataFieldService;
import com.slipper.system.flow.bo.*;
import com.slipper.system.flow.service.*;
import com.slipper.util.Tool;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BpmnEngine {
    @Resource
    private FlowLineService flowLineService;
    @Resource
    private FlowNodeService flowNodeService;
    @Resource
    private FlowLineCondService flowLineCondService;
    @Resource
    private FlowInstNodeService flowInstNodeService;
    @Resource
    private CodelessService codelessService;
    @Resource
    private DataFieldService dataFieldService;
    @Resource
    private FlowPendingService flowPendingService;

    /**
     * 进入下一环节
     * @return 返回进入节点数
     */
    public int next(int dealCnt, FlowNodeBO preNode, FlowInstBO flowInst, FlowInstNodeBO preInstNode) {
        List<FlowNodeBO> flowNodeList = findNextNode(preNode, flowInst, preInstNode);
        if (flowNodeList.isEmpty()) throw new CommonException("无法确定下一环节。");

        for (FlowNodeBO flowNode : flowNodeList) {
            BpmnVO vo = new BpmnVO();
            resolveDealMan(vo, flowNode, flowInst, preInstNode);
            INodeDealer dealer = NodeDealerFactory.getNodeDealer(flowNode.getType());
            dealer.doDeal(dealCnt + 1, false, flowNode, flowInst, preInstNode, null, vo);
        }

        return flowNodeList.size();
    }

    /**
     * 查询流转的下一节点
     */
    public List<FlowNodeBO> findNextNode(FlowNodeBO flowNode, FlowInstBO flowInst, FlowInstNodeBO flowInstNode) {
        FlowLineBO flowLineQO = new FlowLineBO();
        flowLineQO.setFlowId(flowNode.getFlowId());
        flowLineQO.setPreNodeId(flowNode.getId());
        List<FlowLineBO> flowLineList = flowLineService.queryList(flowLineQO);
        if (flowLineList.isEmpty()) throw new CommonException("节点【" + flowNode.getName() + "】无流转线路！");

        List<FlowNodeBO> flowNodeList = new ArrayList<>();
        switch (flowNode.getType()) {
            case FlowConstants.FlowNode.TYPE_TASK,
                 FlowConstants.FlowNode.TYPE_SCRIPT,
                 FlowConstants.FlowNode.PARALLEL_NODE,
                 FlowConstants.FlowNode.MERGE_NODE,
                 FlowConstants.FlowNode.TYPE_END -> {
                for (FlowLineBO flowLine : flowLineList) {
                    if (Tool.isBlank(flowLine.getNextNodeId())) continue;
                    FlowNodeBO nextNode = flowNodeService.queryBy(flowLine.getNextNodeId());
                    if (nextNode == null) continue;
                    nextNode.setInLineId(flowLine.getId());
                    flowNodeList.add(nextNode);
                }
            }
            case FlowConstants.FlowNode.TYPE_BRANCH -> {
                for (FlowLineBO flowLine : flowLineList) {
                    if (Tool.isBlank(flowLine.getNextNodeId())) continue;
                    if (!isNextLine(flowNode, flowInst, flowInstNode, flowLine)) continue;
                    FlowNodeBO nextNode = flowNodeService.queryBy(flowLine.getNextNodeId());
                    if (nextNode == null) continue;
                    nextNode.setInLineId(flowLine.getId());
                    flowNodeList.add(nextNode);
                }
            }
        }

        return flowNodeList;
    }

    /**
     * 是否为下一线路
     * @param flowLine 线路信息
     * @return true/false
     */
    public boolean isNextLine(FlowNodeBO flowNode, FlowInstBO flowInst, FlowInstNodeBO flowInstNode, FlowLineBO flowLine) {
        if (flowLine == null) return false;
        FlowLineCondBO flowLineCondQO = new FlowLineCondBO();
        flowLineCondQO.setLineId(flowLine.getId());
        List<FlowLineCondBO> conditionList = flowLineCondService.queryList(flowLineCondQO);
        if (conditionList.isEmpty()) return false;
        conditionList.forEach(item -> item.setCondGroup(item.getCondGroup() == null ? 93517 : item.getCondGroup()));
        Map<Integer, List<FlowLineCondBO>> conditionMap = conditionList.stream()
                .collect(Collectors.groupingBy(FlowLineCondBO::getCondGroup));
        for (List<FlowLineCondBO> list : conditionMap.values()) {
            if (isPassCondition(flowNode, flowInst, flowInstNode, list)) return true;
        }
        return false;
    }

    private boolean isPassCondition(
            FlowNodeBO flowNode,
            FlowInstBO flowInst,
            FlowInstNodeBO flowInstNode,
            List<FlowLineCondBO> conditionList) {
        for(FlowLineCondBO condition : conditionList) {
            if (Tool.isBlank(condition.getCondType())) return false;

            if (FlowConstants.FlowLineCond.COND_ORIGIN_SCRIPT.equals(condition.getCondOrigin())) {
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("flowId", flowNode.getFlowId());
                paramMap.put("currNodeId", flowNode.getId());
                paramMap.put("currInstId", flowInst.getId());
                Object result = GroovyService.runById(condition.getScriptId(), paramMap);
                if (!(result instanceof Boolean) || !(Boolean) result) return false;
            } else if (FlowConstants.FlowLineCond.COND_ORIGIN_FIELD.equals(condition.getCondOrigin())) {
                String nodeFieldData = getNodeFieldData(condition.getNodeId(), condition.getFieldName(), flowInstNode);
                String fieldType = dataFieldService.queryFieldType(condition.getModelId(), condition.getFieldName());
                if ("整数".equals(fieldType) || "小数".equals(fieldType)) {
                    BigDecimal condValue = new BigDecimal(condition.getCondValue());
                    BigDecimal fieldValue = nodeFieldData == null ? new BigDecimal(0) : new BigDecimal(nodeFieldData);
                    if (!((condition.getCondType().equals("小于") && fieldValue.compareTo(condValue) < 0)
                            || (condition.getCondType().equals("小于等于") && fieldValue.compareTo(condValue) <= 0)
                            || (condition.getCondType().equals("等于") && fieldValue.compareTo(condValue) == 0)
                            || (condition.getCondType().equals("大于") && fieldValue.compareTo(condValue) > 0)
                            || (condition.getCondType().equals("大于等于") && fieldValue.compareTo(condValue) >= 0)
                            || (condition.getCondType().equals("不等于") && fieldValue.compareTo(condValue) != 0))) {
                        return false;
                    }
                } else {
                    String condValue = condition.getCondValue() == null ? "" : condition.getCondValue();
                    String fieldValue = nodeFieldData == null ? "" : nodeFieldData;
                    if (!((condition.getCondType().equals("包含") && fieldValue.contains(condValue))
                            || (condition.getCondType().equals("不包含") && !fieldValue.contains(condValue))
                            || (condition.getCondType().equals("小于") && fieldValue.compareTo(condValue) < 0)
                            || (condition.getCondType().equals("小于等于") && fieldValue.compareTo(condValue) <= 0)
                            || (condition.getCondType().equals("等于") && fieldValue.equals(condValue))
                            || (condition.getCondType().equals("大于") && fieldValue.compareTo(condValue) > 0)
                            || (condition.getCondType().equals("大于等于") && fieldValue.compareTo(condValue) >= 0)
                            || (condition.getCondType().equals("不等于") && !fieldValue.equals(condValue)))) {
                        return false;
                    }
                }
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * 解释节点处理人
     */
    public void resolveDealMan(BpmnVO vo, FlowNodeBO flowNode, FlowInstBO flowInst, FlowInstNodeBO preInstNode) {
        if (!FlowConstants.FlowNode.TYPE_TASK.equals(flowNode.getType())) return;

        if (Tool.isNotBlank(flowNode.getDealManType()) && Tool.isNotBlank(flowNode.getDealManSourceId())) {
            switch (flowNode.getDealManType()) {
                case FlowConstants.FlowNode.DEAL_MAN_TYPE_MANUALLY -> vo.setCurrEmpId(flowNode.getDealManSourceId());
                case FlowConstants.FlowNode.DEAL_MAN_TYPE_NODE_DEALER ->
                    vo.setCurrEmpId(getNodeDealManId(flowNode.getDealManSourceId(), preInstNode));
                case FlowConstants.FlowNode.DEAL_MAN_TYPE_NODE_DATA ->
                    vo.setCurrEmpId(getNodeFieldData(flowNode.getDealManNodeId(), flowNode.getDealManSourceId(), preInstNode));
                case FlowConstants.FlowNode.DEAL_MAN_TYPE_SCRIPT -> {
                    Map<String, Object> paramMap = new HashMap<>();
                    paramMap.put("flowId", flowNode.getFlowId());
                    paramMap.put("currNodeId", flowNode.getId());
                    paramMap.put("currInstId", flowInst.getId());
                    vo.setCurrEmpId((String) GroovyService.runById(flowNode.getDealManSourceId(), paramMap));
                }
            }
        } else if (Tool.isNotBlank(flowNode.getDealOrgType()) && Tool.isNotBlank(flowNode.getDealOrgSourceId())) {
            switch (flowNode.getDealOrgType()) {
                case FlowConstants.FlowNode.DEAL_ORG_TYPE_MANUALLY -> vo.setCurrOrgId(flowNode.getDealOrgSourceId());
                case FlowConstants.FlowNode.DEAL_ORG_TYPE_NODE_DEALER ->
                    vo.setCurrOrgId(getNodeDealOrgId(flowNode.getDealManSourceId(), preInstNode));
                case FlowConstants.FlowNode.DEAL_ORG_TYPE_NODE_DATA ->
                    vo.setCurrOrgId(getNodeFieldData(flowNode.getDealManNodeId(), flowNode.getDealManSourceId(), preInstNode));
                case FlowConstants.FlowNode.DEAL_ORG_TYPE_SCRIPT -> {
                    Map<String, Object> paramMap = new HashMap<>();
                    paramMap.put("flowId", flowNode.getFlowId());
                    paramMap.put("currNodeId", flowNode.getId());
                    paramMap.put("currInstId", flowInst.getId());
                    vo.setCurrOrgId((String) GroovyService.runById(flowNode.getDealManSourceId(), paramMap));
                }
            }
        }

        if (Tool.isBlank(vo.getCurrOrgId()) && Tool.isBlank(vo.getCurrEmpId())) {
            throw new CommonException("节点【" + flowNode.getName() + "】处理人未配置！");
        }
    }

    /**
     * 获取节点处理人
     * @param nodeId 流程节点id
     * @param flowInstNode 实例节点
     */
    public String getNodeDealManId(String nodeId, FlowInstNodeBO flowInstNode) {
        FlowInstNodeBO instNode = findPreInstNode(nodeId, flowInstNode);
        if (instNode == null || Tool.isBlank(instNode.getDealOrgId()) || Tool.isBlank(instNode.getDealManId())) return null;
        return instNode.getDealOrgId() + "@" + instNode.getDealManId();
    }

    /**
     * 获取节点处理部门
     * @param nodeId 流程节点id
     * @param flowInstNode 实例节点
     */
    public String getNodeDealOrgId(String nodeId, FlowInstNodeBO flowInstNode) {
        FlowInstNodeBO preInstNode = findPreInstNode(nodeId, flowInstNode);
        if (preInstNode == null || Tool.isBlank(preInstNode.getDealOrgId())) return null;
        return preInstNode.getDealOrgId();
    }

    /**
     * 获取节点数据
     * @param nodeId 流程节点id
     * @param fieldName 字段名
     * @param flowInstNode 实例节点
     */
    public String getNodeFieldData(String nodeId, String fieldName, FlowInstNodeBO flowInstNode) {
        FlowInstNodeBO preInstNode = findPreInstNode(nodeId, flowInstNode);
        if (preInstNode == null || Tool.isBlank(preInstNode.getDataId())) return null;

        FlowNodeBO flowNode = flowNodeService.queryBy(nodeId);
        if (flowNode == null || Tool.isBlank(flowNode.getModelId())) return null;

        Map<String, Object> dataMap = codelessService.queryBy(flowNode.getModelId(), preInstNode.getDataId());
        Object dataValue = dataMap == null ? null : dataMap.get(fieldName);
        return dataValue == null ? null : dataValue.toString();
    }

    /**
     * 根据节点id查找前一实例节点
     * @param nodeId 节点
     * @param flowInstNode 实例节点
     * @return 实例节点
     */
    public FlowInstNodeBO findPreInstNode(String nodeId, FlowInstNodeBO flowInstNode) {
        if (Tool.isBlank(nodeId) || flowInstNode == null) return null;
        if (nodeId.equals(flowInstNode.getCurrFlowNodeId())) return flowInstNode;
        if (Tool.isBlank(flowInstNode.getPreInstNodeId())) return null;

        List<FlowInstNodeBO> instNodeList = flowInstNodeService.queryListByInstId(flowInstNode.getInstId());
        return findFlowInstNode(nodeId, flowInstNode.getPreInstNodeId(), null, 0, instNodeList);
    }

    /**
     * 根据节点id查找实例节点
     * @param nodeId 节点
     * @param instNodeId 实例节点id
     * @param dealManId 实例节点处理人
     * @param findCnt 查找实例节点数
     * @param instNodeList 实例节点列表
     * @return 实例节点
     */
    public FlowInstNodeBO findFlowInstNode(
            String nodeId,
            String instNodeId,
            String dealManId,
            int findCnt,
            List<FlowInstNodeBO> instNodeList) {
        if (Tool.isBlank(nodeId) || Tool.isBlank(instNodeId) || instNodeList.isEmpty()) return null;
        if (findCnt > 10000) return null; // 防止死循环

        FlowInstNodeBO instNode = null;
        for (FlowInstNodeBO flowInstNode : instNodeList) {
            if (instNodeId.equals(flowInstNode.getId())) {
                instNode = flowInstNode;
                break;
            }
        }

        if (instNode == null) return null;

        if (nodeId.equals(instNode.getCurrFlowNodeId())
                && (Tool.isBlank(dealManId) || dealManId.equals(instNode.getDealManId()))) {
            return instNode;
        } else {
            return findFlowInstNode(nodeId, instNode.getPreInstNodeId(), dealManId, findCnt + 1, instNodeList);
        }
    }

    /**
     * 根据实例节点id查询此节点最近的汇聚节点
     * @param instNodeId 实例节点id
     * @return 实例节点
     */
    public FlowInstNodeBO findPreParallelInstNode(String instNodeId, int mergeCnt) {
        FlowInstNodeBO preInstNode = flowInstNodeService.queryBy(instNodeId);
        if (preInstNode == null) return null;
        FlowNodeBO flowNode = flowNodeService.queryBy(preInstNode.getCurrFlowNodeId());
        if (flowNode == null) return null;
        if (FlowConstants.FlowNode.PARALLEL_NODE.equals(flowNode.getType())) {
            if (mergeCnt <= 0) return preInstNode;
            mergeCnt--;
        } else if (FlowConstants.FlowNode.MERGE_NODE.equals(flowNode.getType())) {
            mergeCnt++;
        }
        return findPreParallelInstNode(preInstNode.getPreInstNodeId(), mergeCnt);
    }

    /**
     * 查找首人工节点
     * @param flowNodeList 节点列表
     * @param flowLineList 线路列表
     * @return 人工节点
     */
    public FlowNodeBO findFirstTaskNode(List<FlowNodeBO> flowNodeList, List<FlowLineBO> flowLineList) {
        return findFirstTaskNode(null, flowNodeList, flowLineList, new ArrayList<>());
    }

    public FlowNodeBO findFirstTaskNode(
            String curNodeId,
            List<FlowNodeBO> flowNodeList,
            List<FlowLineBO> flowLineList,
            List<FlowNodeBO> passNodeList) {

        if (Tool.isBlank(curNodeId)) { // 当前节点为null，查出开始节点
            FlowNodeBO flowNode = flowNodeList.stream()
                    .filter(item -> FlowConstants.FlowNode.TYPE_START.equals(item.getType()))
                    .findFirst()
                    .orElse(null);
            if (flowNode == null) throw new RuntimeException("无法查询到当前流程开始节点！");
            passNodeList.add(flowNode);
            return this.findFirstTaskNode(flowNode.getId(), flowNodeList, flowLineList, passNodeList);
        } else {
            FlowLineBO flowLine = flowLineList.stream()
                    .filter(item -> curNodeId.equals(item.getPreNodeId()))
                    .findFirst()
                    .orElse(null);
            if (flowLine == null) return null;
            FlowNodeBO flowNode = flowNodeList.stream()
                    .filter(item -> item.getId().equals(flowLine.getNextNodeId()))
                    .findFirst().orElse(null);
            if (flowNode == null) return null;
            passNodeList.add(flowNode);
            if (FlowConstants.FlowNode.TYPE_TASK.equals(flowNode.getType())) {
                return flowNode;
            }

            return this.findFirstTaskNode(flowNode.getId(), flowNodeList, flowLineList, passNodeList);
        }
    }

    /**
     * 核对可对流程做哪些操作
     */
    public void checkOperator(BpmnBO bo) {
        bo.setCanCompleted(false);
        bo.setCanRetrieve(false);

        // 流程未开始/流程不在进行中，不允许取回，不允许办结
        if (bo.isStartFlow()
                || Tool.isBlank(bo.getPendingInstNodeId())
                || bo.getFlowInst() == null
                || !FlowConstants.FlowInst.DEAL_STATUS_UNDERWAY.equals(bo.getFlowInst().getDealStatus())) {
            return;
        }

        // 当前处理节点为自己时，并且当前节点可以进行办结，则可以办结
        if (bo.getFlowInstNode() != null && bo.getCurrEmpId().equals(bo.getFlowInstNode().getDealManId())) {
            bo.setCanCompleted(bo.getFlowNode() != null && Constants.YES.equals(bo.getFlowNode().getCanStop()));
        }

        // 当前实例节点的处理人为当前登录人，待办为已处理，且当前节点可以取回，则可以取回
        FlowPendingBO pending = flowPendingService.queryByInstNodeId(bo.getPendingInstNodeId());
        if (pending != null
                && FlowConstants.FlowPending.STATUS_FINISHED.equals(pending.getStatus())
                && pending.getCurrManId().equals(bo.getCurrEmpId())) {
            FlowInstNodeBO flowInstNode = flowInstNodeService.queryBy(pending.getInstNodeId());
            if (flowInstNode != null) {
                FlowNodeBO flowNode = flowNodeService.queryBy(flowInstNode.getCurrFlowNodeId());
                bo.setCanRetrieve(flowNode != null && Constants.YES.equals(flowNode.getCanBack()));
            }
        }
    }

    public FlowInstNodeBO findManuallyNode(
            Set<String> checkIdSet,
            String instNodeId,
            List<FlowInstNodeBO> instNodeList,
            boolean delete) {
        if (!checkIdSet.add(instNodeId)) return null;

        List<FlowInstNodeBO> preInstNodeList = instNodeList.stream()
                .filter(instNode -> instNode.getId().equals(instNodeId))
                .toList();
        for (FlowInstNodeBO flowInstNodeBO : preInstNodeList) {
            // 并发节点，不处理
            if (flowInstNodeBO.getParallelCnt() != null && flowInstNodeBO.getParallelCnt() > 0) return null;

            FlowNodeBO flowNode = flowNodeService.queryBy(flowInstNodeBO.getCurrFlowNodeId());
            if (flowNode == null) continue;

            // 节点为人工节点直接返回
            if (FlowConstants.FlowNode.TYPE_TASK.equals(flowNode.getType())) return flowInstNodeBO;

            // 节点为非人工节点，删除此节点，继续往上找
            if (delete) flowInstNodeService.delete(flowInstNodeBO);

            FlowInstNodeBO flowInstNode = findManuallyNode(
                    checkIdSet,
                    flowInstNodeBO.getPreInstNodeId(),
                    instNodeList,
                    delete);
            if (flowInstNode != null) return flowInstNode;
        }

        return null;
    }
}
